/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.gmu.javalution.pfronts;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 *
 * @author Jason
 */
public class DebParetoFronts<VEC, VEC_COMPARER extends Comparator<VEC>>
        implements IParetoFront<VEC>
{

    private boolean dirty = false;
    private final Map<Integer, Collection<VEC>> front2vecs =
                                                new TreeMap<Integer, Collection<VEC>>();
    private final Map<VEC, Integer> vec2front = new HashMap<VEC, Integer>();
    private final List<VEC> vectors = new ArrayList<VEC>();
    private final int maxNumFronts;
    private final VEC_COMPARER comparer;

    public DebParetoFronts(VEC_COMPARER comparer, int maxNumFronts)
    {
        this.maxNumFronts = maxNumFronts;
        this.comparer = comparer;
    }

    @Override
    public void add(VEC vector)
    {
        dirty = true;
        vectors.add(vector);
    }

    @Override
    public Collection<VEC> getFront(int index)
    {
        if (dirty)
        {
            paretoSort();
            dirty = false;
        }

        return front2vecs.get(index);
    }

    @Override
    public int getNumberOfFronts()
    {
        if (dirty)
        {
            paretoSort();
            dirty = false;
        }
        return front2vecs.size();
    }

    @Override
    public String toString()
    {
        if (dirty)
        {
            paretoSort();
            dirty = false;
        }
        String s = "";
        int frontIndex = 0;
        for (Integer rank : front2vecs.keySet())
        {
            Collection<VEC> front = front2vecs.get(rank);
            s += (frontIndex++) + "] => [";
            for (VEC vec : front)
            {
                if (vec instanceof double[])
                {
                    s += Arrays.toString(((double[]) vec)) + '\t';
                }
                else
                {
                    s += vec.toString();
                }
            }

            s = s.trim();
            s += "]\n";
        }
        return s;
    }

    private void paretoSort()
    {
        front2vecs.clear();
        vec2front.clear();

        //	Mark number unranked
        int num_unranked = vectors.size();

        //	Mark the rank we are currently assigning.
        int cur_rank = 0;

        //	A temporary array.
        List<VEC> temp_list = new ArrayList<VEC>();

        while (num_unranked > 0 && cur_rank <= maxNumFronts - 1)
        {
            //	Container for the current front
            List<VEC> list_front = new ArrayList<VEC>();

            for (VEC ind : vectors)
            {
                if (!vec2front.containsKey(ind) || vec2front.get(ind) == -1)
                //if (ind.getParetoRank() == -1)
                {
                    //	If individual's rank has not been
                    //	assigned, then set it to current rank
                    //	and try it out.
                    vec2front.put(ind, cur_rank);
//                    ind.setParetoRank(cur_rank);
                    list_front.add(ind);

                    //	Now iterate over all candidates,
                    //	checking the new guy for dominance
                    boolean b_break = false;
                    for (Iterator<VEC> f = list_front.iterator(); f.hasNext();)
                    {
                        VEC tf = f.next();

                        if (tf != ind)
                        {
                            int comparison = this.comparer.compare(ind, tf);
//                            switch (ind.checkConstrainedDominance(tf))
                            switch (comparison)
                            {
                                case -1:
//                                case DOMINATING:
                                {
                                    vec2front.put(tf, -1);
//                                    tf.setParetoRank(-1);
                                    f.remove();
                                    break;
                                }
//                                case DOMINATED:
                                case 1:
                                {
                                    vec2front.put(ind, -1);
//                                    ind.setParetoRank(-1);
                                    list_front.remove(ind);
                                    b_break = true;
                                    break;
                                }
//                                case INCOMPARABLE:
                                case 0:
                                {
                                    break;
                                }
                                default:
                                    throw new IllegalArgumentException("Unhandled: " +
                                            comparison);

                            }

                            if (b_break)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            if (list_front.isEmpty())
            {
                break;
            }

            //	Move the individuals to temp
            temp_list.addAll(list_front);

            //	Remove them from individuals
            for (Iterator<VEC> it = vectors.iterator(); it.hasNext();)
            {
                if (list_front.contains(it.next()))
                {
                    it.remove();
                }
            }

            num_unranked -= list_front.size();

            list_front.clear();

            cur_rank++;
        }

        vectors.addAll(temp_list);

        for (VEC vector : vectors)
        {
            //if we're tracking less fronts than we have, we might not have
            //recorded this vector's pareto rank
            if (vec2front.containsKey(vector))
            {
                int rank = vec2front.get(vector);
                if (rank == -1)
                {
                    vec2front.remove(vector);
                }
                else
                {
                    if (!front2vecs.containsKey(rank))
                    {
                        front2vecs.put(rank, new ArrayList<VEC>());
                    }
                    front2vecs.get(rank).add(vector);
                }
            }
        }

        temp_list.clear();
    }
}
